package cn.sciento.farm.devicemanager.service.impl;

import cn.sciento.farm.devicemanager.common.constant.DeviceConstant;
import cn.sciento.farm.devicemanager.common.constant.LoraWanConstant;
import cn.sciento.farm.devicemanager.common.constant.RedisConstant;
import cn.sciento.farm.devicemanager.common.util.RedisUtil;
import cn.sciento.farm.devicemanager.dao.entity.DevCamera;
import cn.sciento.farm.devicemanager.dao.entity.DevCollection;
import cn.sciento.farm.devicemanager.dao.entity.DevController;
import cn.sciento.farm.devicemanager.dao.entity.DevDevice;
import cn.sciento.farm.devicemanager.dao.mapper.CollectionMapper;
import cn.sciento.farm.devicemanager.dao.mapper.ControllerMapper;
import cn.sciento.farm.devicemanager.dao.mapper.CameraMapper;
import cn.sciento.farm.devicemanager.dao.mapper.DeviceMapper;
import cn.sciento.farm.devicemanager.dao.model.DeviceStatistics;
import cn.sciento.farm.devicemanager.gateway.lorawan.grpc.GatewayService;
import cn.sciento.farm.devicemanager.gateway.lorawan.grpc.LoraWanDeviceService;
import cn.sciento.farm.devicemanager.gateway.uplink.service.lorawan.LorawanHeartListener;
import cn.sciento.farm.devicemanager.lorawan.*;
import cn.sciento.farm.devicemanager.lorawan.loraserver.Location;
import cn.sciento.farm.devicemanager.service.CollectionService;
import cn.sciento.farm.devicemanager.service.ControllerService;
import cn.sciento.farm.devicemanager.service.DeviceService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private CollectionMapper collectionMapper;

    @Autowired
    private ControllerMapper controllerMapper;

    @Autowired
    private ControllerService controllerService;

    @Autowired
    private CollectionService collectionService;

    @Autowired
    private CameraMapper cameraMapper;

    @Autowired
    private LoraWanDeviceService loraWanDeviceService;

    @Autowired
    private GatewayService gatewayService;


    @Autowired
    private LorawanHeartListener lorawanHeartListener;

    @Override
    public DevDevice getById(String eui) {
        String deviceStr = RedisUtil.getCacheStr(RedisConstant.CONCENTRATOR_CACHE + eui);
        if (deviceStr == null) {
            DevDevice device = deviceMapper.selectById(eui);
            if (device == null) {
                return null;
            }
            RedisUtil.putCacheStr(RedisConstant.CONCENTRATOR_CACHE + eui, JSON.toJSONString(device), RedisConstant.MINUTES_CACHE);
            return device;
        } else {
            return JSON.parseObject(deviceStr,DevDevice.class);
        }

    }

    @Override
    public int updateStatus(String eui, int status) {
        DevDevice devDevice = new DevDevice();
        devDevice.setId(eui);
        devDevice.setStatus(status);
        RedisUtil.cleanRedisByKey(RedisConstant.CONCENTRATOR_CACHE + eui);
        return deviceMapper.updateById(devDevice);
    }

    @Override
    public int updateOnline(String eui, int up) {
        DevDevice device = new DevDevice();
        device.setId(eui);
        device.setOnline(up);
        RedisUtil.cleanRedisByKey(RedisConstant.CONCENTRATOR_CACHE + eui);
        return deviceMapper.updateById(device);
    }

    @Override
    public int updateSysById(DevDevice oldConcentrator) {
        int count = deviceMapper.updateById(oldConcentrator);
        RedisUtil.cleanRedisByKey(RedisConstant.CONCENTRATOR_CACHE + oldConcentrator.getId());
        return count;
    }

    @Override
    public int insert(DevDevice devDevice) {
        devDevice.setId(devDevice.getId().trim());
        String deviceType = devDevice.getType();
        switch (deviceType) {
            case DeviceConstant.LORAWAN_GW:
                return insertLorawanGW(devDevice);
            case DeviceConstant.LOEAWAN_NODE:
                return insertLorawanNode(devDevice);
            case DeviceConstant.SCI_GW:
                return insertSCIGW(devDevice);
            case DeviceConstant.INSENTEK_GW:
                return insertInsentekGW(devDevice);
            default:
                // 不对下兼容
                return 0;
        }
    }

    private int insertInsentekGW(DevDevice deviceConForm){
        deviceConForm.setOnline(1);
        int count = deviceMapper.insert(deviceConForm);
        return count;
    }

    private int insertSCIGW(DevDevice deviceConForm) {
        int count = deviceMapper.insert(deviceConForm);

        return count;
    }

    private int insertLorawanNode(DevDevice deviceConForm) {
        // todo 这里固定使用同一个device profile  后期可以扩展
        deviceConForm.setProfileId(LoraWanConstant.DEVICE_PROFILE);
        try {
            loraWanDeviceService.create(CreateDeviceRequest.newBuilder()
                    .setDevice(Device.newBuilder()
                            .setApplicationId(LoraWanConstant.APPLICATION_ID)
                            .setDescription(deviceConForm.getRemark() == null ? "" : deviceConForm.getRemark())
                            .setName(RandomStringUtils.randomAlphanumeric(5)+deviceConForm.getName())
                            .setDeviceProfileId(LoraWanConstant.DEVICE_PROFILE)
                            .setDevEui(deviceConForm.getId())
                            .build())
                    .build());
        }catch (StatusRuntimeException e){
            if(e.getStatus().getCode().equals(Status.ALREADY_EXISTS.getCode())){
                int count = deviceMapper.insert(deviceConForm);
                return count;
            }else{
                e.printStackTrace();
                throw e;
            }
        }
        try {
            String key = "";
            if(StringUtils.isEmpty(deviceConForm.getDevSecret())){
                key = LoraWanConstant.APP_KEY;
            }else {
                key = deviceConForm.getDevSecret();
            }
            loraWanDeviceService.createKeys(CreateDeviceKeysRequest.newBuilder()
                    .setDeviceKeys(DeviceKeys.newBuilder()
                            .setAppKey(key)
                            .setNwkKey(key)
                            .setDevEui(deviceConForm.getId())
                            .build())
                    .build()
            );
        } catch (Exception e) {
            loraWanDeviceService.delete(DeleteDeviceRequest.newBuilder()
                    .setDevEui(deviceConForm.getId())
                    .build()
            );
            e.printStackTrace();
            throw e;
        }

        int count = deviceMapper.insert(deviceConForm);
        return count;
    }

    private int insertLorawanGW(DevDevice deviceConForm) {
        String address = deviceConForm.getLocation();
        if(address == null) {
            return 0;
        }
        List<Double> addrList = Arrays.stream(address.substring(1, address.length() - 1).split(","))
                .map(Double::parseDouble).collect(Collectors.toList());
        CreateGatewayRequest request = CreateGatewayRequest.newBuilder()
                .setGateway(Gateway.newBuilder()
                        .setName(RandomStringUtils.randomAlphanumeric(5)+deviceConForm.getName())
                        .setDescription(deviceConForm.getRemark() != null ? deviceConForm.getRemark() : "")
                        .setGatewayProfileId(LoraWanConstant.GATEWAY_PROFILE_ID)
                        .setNetworkServerId(LoraWanConstant.NETWORK_SERVER_ID)
                        .setOrganizationId(LoraWanConstant.ORGANIZATION_ID)
                        .setId(deviceConForm.getId())
                        .setLocation(Location.newBuilder()
                                .setAltitude(deviceConForm.getAltitude())
                                .setLatitude(addrList.get(0))
                                .setLongitude(addrList.get(1))
                                .build())
                        .build())
                .build();
        gatewayService.create(request);
        return deviceMapper.insert(deviceConForm);
    }

    @Override
    public int updateById(DevDevice devDevice) {
        RedisUtil.cleanRedisByKey(RedisConstant.CONCENTRATOR_CACHE + devDevice.getId());
        return deviceMapper.updateById(devDevice);
    }

    @Override
    public List<DevDevice> getByLandId(int landId) {
        QueryWrapper<DevDevice> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("land_id",landId);
        queryWrapper.orderByDesc("sequence","create_time");
        return deviceMapper.selectList(queryWrapper);
    }

    @Transactional(rollbackFor = {Exception.class})
    @Override
    public int deleteById(String eui) {
        DevDevice devDevice = deviceMapper.selectById(eui);
        if (devDevice == null) {
            return 0;
        }
        if (DeviceConstant.LORAWAN_GW.equals(devDevice.getType())) {
            DeleteGatewayRequest gatewayRequest = DeleteGatewayRequest.newBuilder()
                    .setId(devDevice.getId())
                    .build();
            int count = gatewayService.delete(gatewayRequest);
//            if (count == 0) {
//                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//            }
        } else if (DeviceConstant.LOEAWAN_NODE.equals(devDevice.getType())) {
            DeleteDeviceRequest deleteDeviceRequest = DeleteDeviceRequest.newBuilder()
                    .setDevEui(devDevice.getId())
                    .build();

            int count = loraWanDeviceService.delete(deleteDeviceRequest);
//            if (count == 0) {
//                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//            }
        }
        lorawanHeartListener.delete(eui);
        RedisUtil.cleanRedisByKey(RedisConstant.CONCENTRATOR_CACHE + eui);
        return deviceMapper.deleteById(eui);
    }

    @Override
    public List<DevDevice> getByAreaId(int areaId) {
        QueryWrapper<DevDevice> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("area_id", areaId);
        queryWrapper.orderByDesc("sequence", "create_time");
        return deviceMapper.selectList(queryWrapper);
    }

    @Override
    public DeviceStatistics getStatistics(long areaId) {

        //网关
        QueryWrapper<DevDevice> conWrapper = new QueryWrapper<>();
        conWrapper.eq("area_id", areaId);
        List<DevDevice> devDeviceList = deviceMapper.selectList(conWrapper);
        int deviceAll = devDeviceList.size();
        List<String> deviceIdList = devDeviceList.stream().map(DevDevice::getId).collect(Collectors.toList());
        int deviceOnline = (int) devDeviceList.stream().filter((DevDevice item) -> {
            return item.getOnline() == 1;
        }).count();


        //采集
        int collectionAll =0;
        int collectionOnline = 0;
        if(devDeviceList.size() > 0){
            QueryWrapper<DevCollection> collectionWrapper = new QueryWrapper<>();
            collectionWrapper.in("eui", deviceIdList);
            collectionAll = collectionMapper.selectCount(collectionWrapper);

            collectionWrapper.eq("online", 1);
            collectionOnline = collectionMapper.selectCount(collectionWrapper);
        }


        //开关
        int controllerAll = 0;
        int controllerOnline = 0;
        if(devDeviceList.size() > 0){
            QueryWrapper<DevController> switchWrapper = new QueryWrapper<>();

            switchWrapper.in("eui", deviceIdList);
            controllerAll= controllerMapper.selectCount(switchWrapper);

            switchWrapper.eq("online", 1);
            controllerOnline = controllerMapper.selectCount(switchWrapper);
        }




        //摄像头
        QueryWrapper<DevCamera> cameraWrapper = new QueryWrapper<>();
        cameraWrapper.eq("area_id", areaId);
        int cameraAll = cameraMapper.selectCount(cameraWrapper);


        DeviceStatistics deviceStatistics = new DeviceStatistics();
        deviceStatistics.setAllCamera(cameraAll);
        deviceStatistics.setAllCollection(collectionAll);
        deviceStatistics.setOnlineCollection(collectionOnline);
        deviceStatistics.setAllController(controllerAll);
        deviceStatistics.setOnlineController(controllerOnline);
        deviceStatistics.setAllDevice(deviceAll);
        deviceStatistics.setOnlineDevice(deviceOnline);

        return deviceStatistics;
    }

    @Override
    public List<DevDevice> getBySceneId(Long id) {
        return deviceMapper.getBySceneId(id);
    }

    @Override
    public int deleteByArea(Integer areaId,Integer landId) {
        QueryWrapper<DevDevice> queryWrapper = new QueryWrapper<>();
        List<DevDevice> devDeviceList = new ArrayList<>();
        if(landId != null){
            queryWrapper.eq("land_id",landId);
            devDeviceList = getByLandId(landId);
        }else if(areaId != null){
            queryWrapper.eq("area_id",areaId);
            devDeviceList  = getByAreaId(areaId);

        }else {
            throw new NullPointerException("基地和地块id不能同时为空");
        }
        // 删除采集，这里添加了外键约束，可以不写


        // 删除控制，这里必须写，要删除定时和策略
        devDeviceList.forEach(device -> {
            controllerService.deleteByEui(device.getId());
        });

        return deviceMapper.delete(queryWrapper);
    }

}
